import os
import datetime
import itertools

import db
from model import Stock, Transaction, Price, WishList, commit
import fetchers

def o2d(o): return dict (i for i in o.__dict__.items() if i[0][0] != '_')
def s2d(o):
    ret = dict (i for i in o.__dict__.items() if i[0][0] != '_')
    ret['suggestion'] = o.suggestion
    ret['quantity'] = o.quantity
    ret['highest_price'] = o.highest_price
    ret['current_price'] = o.current_price
    ret['ur_profit'] = o.ur_profit
    ret['r_profit'] = o.r_profit
    return ret

class ValidationError(Exception): pass

def listStocks():
    ''' returns list of known stocks '''
    return [e.symbol for e in Stock.query.all()]

def listStocksDetails():
    return [s2d(e) for e in Stock.query.all()]

def getStockInfo(symbol):
    s = Stock.get(symbol)
    return s2d(s)

def prepareFinYearReport(year):
    yr_start = datetime.datetime(year - 1, 4, 1)
    yr_end = datetime.datetime(year, 3, 31)
    sells = [tr for tr in Transaction.query.filter_by(tr_type="sell") if yr_start < tr.date < yr_end ]
    by_stock = lambda tr: tr.stock
    sells.sort(key=by_stock)

    trs = []
    for (stock, stock_sells) in itertools.groupby(sells, by_stock):
        stock_sells = list(stock_sells)
        trs.extend(stock_sells)
        qty = sum([sell.quantity for sell in stock_sells])
        latest_sell = sorted(stock_sells, key = lambda sell: sell.date)[-1]
        buys = [tr for tr in sorted(stock.transactions, key = lambda tr: tr.date, reverse=True) if tr.tr_type == "buy" and tr.date < latest_sell.date]
        for buy in buys:
            qty += buy.quantity
            trs.append(buy)
            if qty >= 0:
                break

    tr2row = lambda tr: [str(tr.date.date()), tr.stock.symbol, tr.tr_type, tr.price, abs(tr.quantity), tr.profit]
    rows = [tr2row(tr) for tr in trs]
    rows.insert(0, ("Date", "Company", "Transaction Type", "Trade value(incl. brokerage)", "Quantity", "Profit"))
    total_profit = sum([tr.profit for tr in trs if tr.tr_type == "sell"])
    rows.append(("","","","","",total_profit))
    return rows

def addStock(symbol, name, sector, stoploss=10):
    s = None
    if not symbol in listStocks():
        s = Stock(symbol=symbol, name=name, sector=sector, stoploss=stoploss)
        commit()
    return s

def editStock(symbol, name=None, sector=None, stoploss=None):
    s = Stock.get(symbol)
    if name:
        s.name = name
    if sector:
        s.sector = sector
    if stoploss:
        s.stoploss = stoploss
    commit()
    
def removeStock(symbol):
    Stock.get(symbol).delete()
    commit()

def addTransaction(symbol, tr_type, quantity, price, date=None):
    tr_type = tr_type.lower()
    if not date:
        date = datetime.datetime.now()
    if tr_type == "sell":
        quantity = -int(quantity)
    tr = Transaction(stock_symbol=symbol, tr_type=tr_type, price=price, quantity=quantity, date=date)
    p = Price(stock_symbol=symbol, price=float(price)/float(quantity))
    Stock.get(symbol).prices.append(p)
    commit()
    return tr

def buyStock(symbol, purchase_price, quantity, purchase_date=None):
    tr_type = 'buy'
    return addTransaction(symbol, tr_type, purchase_price, quantity, purchase_date)

def sellStock(symbol, sell_price, quantity, sell_date=None):
    tr_type = 'sell'
    return addTransaction(symbol, tr_type, sell_price, -quantity, sell_date)

def defineRules(symbol, stoploss=None, buyprice_range=None):
    s = Stock.get(name)
    if stoploss:
        if stoploss > 99 or stoploss < 1:
            raise ValidationError("Invalid stoploss value %d. (valid range 1-99)" % stoploss)
        s.stoploss = stoploss
        commit()
    if buyprice_range:
        raise NotImplementedError

def updatePrice(symbol):
    price = fetchers.yahoo(symbol)[0][1]
    p = Price(stock_symbol=symbol, price=price)
    Stock.get(symbol).prices.append(p)
    commit()

def updateAllPrices():
    syms = [s.symbol for s in Stock.query.all()][:7]
    price_tuples = fetchers.yahoo(syms)
    while True:
        symbol, price = price_tuples.next()
        p = Price(stock_symbol=symbol, price=price)
        s = Stock.get(symbol)
        s.prices.append(p)
        commit()
        yield s.symbol, s.name, price

def addToWishList(symbol, target_price):
    s = Stock.get(symbol)
    w = WishList(target_price=target_price)
    s.wish = w
    commit()
    return w

def backup(backup_dir):
    src = db.dbpath
    dst = backup_dir
    if not os.path.isdir(backup_dir):
        raise ValidationError("Backup Directory path '%s' invalid or not a directory" % dst)
    cmd = "/bin/cp -v %(src)s %(dst)s" % locals()
    return os.system(cmd) == 0

def restore(backup_dir):
    src = backup_dir
    dst = db.dbpath
    if not os.path.isdir(backup_dir):
        raise ValidationError("Backup Directory path '%s' invalid or not a directory" % src)
    cmd = "/bin/cp -iv %(src)s/profile.sqlite %(dst)s" % locals()
    return os.system(cmd) == 0
